package server.connector.http;

import org.apache.catalina.util.StringManager;
import server.ServletProcessor;
import server.StaticResourceProcessor;
import server.util.RequestUtil;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import java.io.OutputStream;
import java.net.Socket;

public class HttpProcessor {
    private HttpConnector connector;
    private HttpRequest request;
    private HttpResponse response;
    private HttpRequestLine requestLine = new HttpRequestLine();
    protected StringManager sm = StringManager.getManager(Constants.PACKAGE);
    public HttpProcessor(HttpConnector connector) {
        this.connector = connector;
    }

    /**
     * 处理Http连接
     * @param socket 建立连接的Socket
     */

    public void process(Socket socket) {
        SocketInputStream input = null;
        OutputStream output = null;
        try {
            input = new SocketInputStream(socket.getInputStream(), 2048);
            output = socket.getOutputStream();

            //创建HttpRequest对象
            request = new HttpRequest(input);
            parseRequestLine(input);
            parseHeaders(input);

            //创建HttpResponse对象
            response = new HttpResponse(output);
            response.setRequest(request);
            response.setHeader("Server", "WebServer By cyl");

            //处理静态资源或Servlet请求
            if (request.getRequestURI().startsWith("/servlet/")) {
                new ServletProcessor().process(request, response);
            } else {
                new StaticResourceProcessor().process(request, response);
            }
            //后续写 3
//            System.out.println("--------------------------------");
//            System.out.println(request.getMethod() + " " + request.getRequestURI() + " " + request.getProtocol());
//            for (HashMap.Entry<String, ArrayList<String>> entry : request.headers.entrySet()) {
//                System.out.println(entry.getKey() + ": " + entry.getValue());
//            }
//            System.out.println("---------");
//            System.out.println("queryString:" + request.getQueryString());
//            System.out.println("type:" + request.getContentType());
//            System.out.println("length:" + request.getContentLength());
//            System.out.println("参数----------------");
//            System.out.println("getParameterMap()");
//            Map<String, String[]> parameters = request.getParameterMap();
//            for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
//                System.out.println(entry.getKey() + ":" + Arrays.toString(entry.getValue()));
//            }
//            System.out.println("-----");
//            System.out.println("getParameter(String name)");
//            System.out.println(request.getParameter("a"));
//            System.out.println("-----");
//            System.out.println("getParameterValues(String name)");
//            System.out.println(Arrays.toString(request.getParameterValues("a")));
//            System.out.println("-----");
//            System.out.println("getParameterNames()");
//            Enumeration parameterNames = request.getParameterNames();
//            while (parameterNames.hasMoreElements()) {
//                System.out.println(parameterNames.nextElement());
//            }
//            System.out.println("--------------------");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 从输入流中解析出请求行的内容
     * 传入输入流，从输入流中读一行数据，解析出 method,uri,protocol
     * 将uri中的查询字符串、sessionID分离出来，并修正uri
     * 将这些解析出的数据填充到request对象中
     * @param input SocketInputStream类的实例
     * @throws Exception 处理过程中出现的异常
     */
    private void parseRequestLine(SocketInputStream input) throws Exception{
        //解析请求行，解析结果存在requestLine中
        input.readRequestLine(requestLine);
        //判断请求行格式是否正确
        if (requestLine.methodLength < 1) {
            throw new ServletException("Missing HTTP request method");
        }
        if (requestLine.uriLength < 1) {
            throw new ServletException("Missing HTTP request URI");
        }
        if (requestLine.protocolLength < 1) {
            throw new ServletException("Missing HTTP request protocol");
        }
        String method = new String(requestLine.method, 0, requestLine.methodLength);
        String uri = new String(requestLine.uri, 0, requestLine.uriLength);
        String protocol = new String(requestLine.protocol, 0, requestLine.protocolLength);

        //解析查询字符串
        int questionIndex = uri.indexOf("?");
        if (questionIndex >= 0) {
            request.setQueryString(uri.substring(questionIndex + 1));
            uri = uri.substring(0, questionIndex);
        } else {
            request.setQueryString(null);
        }
        //检查URI是否是绝对地址
        if (!uri.startsWith("/")) {
            int index = uri.indexOf("://");
            if (index != -1) {
                index = uri.indexOf("/", index + 3);
                if (index == 0) {
                    uri = "";
                } else {
                    uri = uri.substring(index);
                }
            }
        }
        //检查URI中是否包含sessionid
        int sessionIdBeginIndex = uri.indexOf(";jsessionid=");
        if (sessionIdBeginIndex >= 0) {
            //找到sessionid
            int uriIndex = sessionIdBeginIndex;
            sessionIdBeginIndex += ";jsessionid=".length();
            int sessionIdEndIndex = uri.indexOf(";", sessionIdBeginIndex);
            String sessionId = null;
            if (sessionIdEndIndex >= 0) {
                sessionId = uri.substring(sessionIdBeginIndex, sessionIdEndIndex);
                uri = uri.substring(0, uriIndex) + uri.substring(sessionIdEndIndex + 1);
            } else {
                sessionId = uri.substring(sessionIdBeginIndex);
                uri = uri.substring(0, uriIndex);
            }
            request.setRequestedSessionId(sessionId);
            request.setSessionIdInRequestedURL(true);
        } else {
            request.setRequestedSessionId(null);
            request.setSessionIdInRequestedURL(false);
        }
        //修正uri
        uri = normalize(uri);
        if (uri == null) {
            throw new ServletException("Invalid URI: " + uri);
        }
        //设置方法，uri，协议
        request.setMethod(method);
        request.setRequestURI(uri);
        request.setProtocol(protocol);
    }

    /**
     * 对传入的路径进行修正
     * @param path
     * @return 路径正常或可修正返回修正后的内容，否则返回null
     */

    protected String normalize(String path) {
        if (path == null)
            return null;
        String normalized = path;
        // %7E或%7e代表~ 修正
        if (normalized.startsWith("/%7E") || normalized.startsWith("/%7e"))
            normalized = "/~" + normalized.substring(4);
        // % / . \是特殊字符，不能以%xx形式存在于uri中
        if ((normalized.indexOf("%25") >= 0)
                || (normalized.indexOf("%2F") >= 0)
                || (normalized.indexOf("%2E") >= 0)
                || (normalized.indexOf("%5C") >= 0)
                || (normalized.indexOf("%2f") >= 0)
                || (normalized.indexOf("%2e") >= 0)
                || (normalized.indexOf("%5c") >= 0)) {
            return null;
        }
        // /.修正为/
        if (normalized.equals("/."))
            return "/";

        // \修正为/
        if (normalized.indexOf('\\') >= 0)
            normalized = normalized.replace('\\', '/');
        // 如果uri不以/开头，加上/
        if (!normalized.startsWith("/"))
            normalized = "/" + normalized;

        // 把//改为/
        while (true) {
            int index = normalized.indexOf("//");
            if (index < 0)
                break;
            normalized = normalized.substring(0, index) +
                    normalized.substring(index + 1);
        }

        // 把/./改为/
        while (true) {
            int index = normalized.indexOf("/./");
            if (index < 0)
                break;
            normalized = normalized.substring(0, index) +
                    normalized.substring(index + 2);
        }

        // 把/../这种相对路径改为实际路径
        while (true) {
            int index = normalized.indexOf("/../");
            if (index < 0)
                break;
            //越出根目录边界
            if (index == 0)
                return (null);
            int index2 = normalized.lastIndexOf('/', index - 1);
            normalized = normalized.substring(0, index2) +
                    normalized.substring(index + 3);
        }

        // 不能存在/...
        if (normalized.indexOf("/...") >= 0)
            return (null);
        return normalized;
    }

    /**
     * 从输入流中解析出请求头的内容放到request的headers中
     * 若请求头中有cookie,把cookie解析出来放到request对象中 若cookie中有sessionID,解析出来放到request中
     * 若请求头中有content-length/content-type,解析出来放到request中
     * @param input
     * @throws Exception
     */
    private void parseHeaders(SocketInputStream input) throws Exception {
        //一个header对象，省空间
        HttpHeader header = new HttpHeader();
        while (true) {
            //读一个请求头
            input.readHeader(header);
            //未读到name
            if (header.nameLength == 0) {
                //请求头全部读完
                if (header.valueLength == 0) {
                    return;
                } else { //异常情况
                    throw new ServletException(sm.getString("httpProcessor.parseHeaders.colon"));
                }

            }
            //处理读到的请求头
            String name = new String(header.name, 0, header.nameLength);
            String value = new String(header.value, 0, header.valueLength);
            //添加到request中
            request.addHeader(name, value);

            if ("cookie".equals(name)) {
                //处理cookie
                Cookie[] cookies = RequestUtil.parseCookie(value);
                for (int i = 0; i < cookies.length; i++) {
                    //从cookie中找sessionID
                    if (cookies[i].getName().equals("jsessionid")) {
                        //目前的情况是cookie的sessionId比URL的优先级高
                        if (!request.isRequestedSessionIdFromCookie()) {
                            request.setRequestedSessionId(cookies[i].getValue());
                            request.setSessionIdInRequestedCookie(true);
                            //有cookie直接无效URL
                            request.setSessionIdInRequestedURL(false);
                        }
                    }
                    //添加cookie
                    request.addCookie(cookies[i]);
                }
            } else if ("content-length".equals(name)) {
                try {
                    int length = Integer.parseInt(value);
                    request.setContentLength(length);
                } catch (Exception e) {
                    throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength"));
                }
            } else if ("content-type".equals(name)) {
                request.setContentType(value);
            }
        }
    }

}
